Skip to content

fix(form-core): Prevent synchronous validators from returning Promises#1987

Merged
crutchcorn merged 3 commits into
TanStack:mainfrom
mhuggins:prevent-synchronous-promises
May 10, 2026
Merged

fix(form-core): Prevent synchronous validators from returning Promises#1987
crutchcorn merged 3 commits into
TanStack:mainfrom
mhuggins:prevent-synchronous-promises

Conversation

@mhuggins
Copy link
Copy Markdown
Contributor

@mhuggins mhuggins commented Jan 9, 2026

🎯 Changes

Passing an asynchronous function to a synchronous validator (i.e.: onBlur, onChange, onDynamic, onSubmit) is a bit of a foot-gun given that it does not produce any typescript errors, but it also results in the form/field validation function running after the core validation logic.

This updates the types for these validator functions on both FormApi and FieldApi to prevent passing a function that returns a Promise.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.

Summary by CodeRabbit

  • Bug Fixes
    • Synchronous validators now enforce stricter type-checking to prevent returning Promises, catching incorrect usage patterns at compile-time and improving developer experience.

Review Change Stack

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jan 9, 2026

🦋 Changeset detected

Latest commit: 6b927d2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@tanstack/form-core Patch
@tanstack/angular-form Patch
@tanstack/form-devtools Patch
@tanstack/lit-form Patch
@tanstack/react-form Patch
@tanstack/solid-form Patch
@tanstack/svelte-form Patch
@tanstack/vue-form Patch
@tanstack/react-form-devtools Patch
@tanstack/solid-form-devtools Patch
@tanstack/react-form-nextjs Patch
@tanstack/react-form-remix Patch
@tanstack/react-form-start Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Passing an asynchronous function to a synchronous validator (i.e.: `onBlur`, `onChange`,
`onDynamic`, `onSubmit`) is a bit of a foot-gun given that it does not produce any typescript
errors, but it also results in the form/field validation function running after the core
validation logic.

To prevent this, update the types for these validator functions on both `FormApi` and
`FieldApi` to prevent passing a function that returns a `Promise`.
@mhuggins mhuggins force-pushed the prevent-synchronous-promises branch from 5924423 to 2198362 Compare January 9, 2026 15:57
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jan 10, 2026

View your CI Pipeline Execution ↗ for commit ec84898

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 1m 10s View ↗
nx run-many --target=build --exclude=examples/** ✅ Succeeded 25s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-10 19:36:22 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jan 10, 2026

More templates

@tanstack/angular-form

npm i https://pkg.pr.new/@tanstack/angular-form@1987

@tanstack/form-core

npm i https://pkg.pr.new/@tanstack/form-core@1987

@tanstack/form-devtools

npm i https://pkg.pr.new/@tanstack/form-devtools@1987

@tanstack/lit-form

npm i https://pkg.pr.new/@tanstack/lit-form@1987

@tanstack/preact-form

npm i https://pkg.pr.new/@tanstack/preact-form@1987

@tanstack/react-form

npm i https://pkg.pr.new/@tanstack/react-form@1987

@tanstack/react-form-devtools

npm i https://pkg.pr.new/@tanstack/react-form-devtools@1987

@tanstack/react-form-nextjs

npm i https://pkg.pr.new/@tanstack/react-form-nextjs@1987

@tanstack/react-form-remix

npm i https://pkg.pr.new/@tanstack/react-form-remix@1987

@tanstack/react-form-start

npm i https://pkg.pr.new/@tanstack/react-form-start@1987

@tanstack/solid-form

npm i https://pkg.pr.new/@tanstack/solid-form@1987

@tanstack/solid-form-devtools

npm i https://pkg.pr.new/@tanstack/solid-form-devtools@1987

@tanstack/svelte-form

npm i https://pkg.pr.new/@tanstack/svelte-form@1987

@tanstack/vue-form

npm i https://pkg.pr.new/@tanstack/vue-form@1987

commit: ec84898

@mhuggins
Copy link
Copy Markdown
Contributor Author

Just pushed an update that should address the lint issue, sorry about that!

Comment thread packages/form-core/src/util-types.ts
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.76%. Comparing base (6892ed0) to head (ec84898).
⚠️ Report is 198 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1987      +/-   ##
==========================================
+ Coverage   90.35%   90.76%   +0.41%     
==========================================
  Files          38       59      +21     
  Lines        1752     2209     +457     
  Branches      444      555     +111     
==========================================
+ Hits         1583     2005     +422     
- Misses        149      183      +34     
- Partials       20       21       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mhuggins
Copy link
Copy Markdown
Contributor Author

Just wanted to follow up to see if there's anything that's needed for this to be considered for acceptance. Thanks! 😄

@LeCarbonator
Copy link
Copy Markdown
Contributor

Core concept looks good to me. I'm mostly waiting to check up on some things like instantiation count and testing some surface-level alternatives. Should be done by the end of the week.

@mhuggins
Copy link
Copy Markdown
Contributor Author

@LeCarbonator Just wanted to follow up to see if there's anything else needed here. I can rebase my branch, just let me know!

Copy link
Copy Markdown
Member

@crutchcorn crutchcorn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome. Sorry for missing it on my end. Got @LeCarbonator's approval privately as well. Thank you for this!

# Conflicts:
#	packages/form-core/src/FieldApi.ts
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR enforces a type-level constraint preventing synchronous validators in TanStack Form from returning Promises. A new RejectPromiseValidator conditional type is introduced and applied to all sync validator callbacks in both FieldApi and FormApi, with type-level tests confirming the constraint works as expected.

Changes

Sync Validator Promise Rejection

Layer / File(s) Summary
Type Utility Foundation
packages/form-core/src/util-types.ts
New RejectPromiseValidator<T> conditional type detects Promise/PromiseLike return types and resolves them to never, preventing Promise-returning validators while preserving any types for mocks.
FieldApi Validator Types
packages/form-core/src/FieldApi.ts
All synchronous validator options (onMount, onChange, onBlur, onSubmit, onDynamic) wrapped with RejectPromiseValidator; import statement updated to include the new type utility.
FormApi Validator Types
packages/form-core/src/FormApi.ts
All synchronous validator properties (onMount, onChange, onBlur, onSubmit, onDynamic) wrapped with RejectPromiseValidator; import statement updated to include the new type utility.
Type-Level Tests
packages/form-core/tests/FieldApi.test-d.ts, packages/form-core/tests/FormApi.test-d.ts
Type tests verify that Promise-returning synchronous validators are rejected with @ts-expect-error assertions on FieldApi and FormApi instances.
Release Documentation
.changeset/silver-mails-play.md
Changeset entry documents patch version release noting synchronous validators will no longer permit Promise returns.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A promise to reject all promiselike things,
sync validators shed their async wings,
TypeScript ensures the contract is tight,
no sneaky returns in the dead of night!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change—adding type constraints to prevent synchronous validators from returning Promises across both FormApi and FieldApi.
Description check ✅ Passed The description clearly explains the motivation, implementation approach, and follows the template with all required sections completed and checkboxes marked appropriately.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@crutchcorn crutchcorn merged commit 01f51b5 into TanStack:main May 10, 2026
8 of 9 checks passed
@github-actions github-actions Bot mentioned this pull request May 10, 2026
@mhuggins
Copy link
Copy Markdown
Contributor Author

Awesome, thanks for getting this merged!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants